package ru.ifmo.tanya.service.impl;

import org.jdom.Element;
import ru.ifmo.tanya.core.calls.Call;
import ru.ifmo.tanya.core.calls.CallManager;
import ru.ifmo.tanya.core.huntgroups.HuntGroup;
import ru.ifmo.tanya.core.users.User;
import ru.ifmo.tanya.core.users.UserRole;
import ru.ifmo.tanya.db.HuntGroupManager;
import ru.ifmo.tanya.keepers.OnlineUsersKeeper;
import ru.ifmo.tanya.keepers.UserStateKeeper;
import ru.ifmo.tanya.keepers.UserStatusKeeper;
import ru.ifmo.tanya.service.XmlService;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class AgentsAndQueuesService extends XmlService {

    private final OnlineUsersKeeper onlineUsersKeeper = new OnlineUsersKeeper();
    private final UserStateKeeper userStateKeeper = new UserStateKeeper();
    private final UserStatusKeeper userStatusKeeper = new UserStatusKeeper();
    private final HuntGroupManager huntGroupManager = new HuntGroupManager();
    private final CallManager callManager = new CallManager();

    @Override
    public void invoke(User user, HttpServletRequest request, Element response) {
        Set<User> onlineUsers = onlineUsersKeeper.getOnlineUsers();
        Set<User> onlineSupervisors = getOnlineUsersByRole(onlineUsers, UserRole.SUPERVISOR);
        Element supervisorsElement = new Element("supervisors");
        for (final User onlineSupervisor : onlineSupervisors) {
            supervisorsElement.addContent(onlineSupervisor.toXml());
        }
        response.addContent(supervisorsElement);

        Set<User> onlineAgents = getOnlineUsersByRole(onlineUsers, UserRole.AGENT);
        List<HuntGroup> huntGroups = huntGroupManager.getAllHuntGroups();
        addHuntGroups(response, huntGroups);

        Element onlineAgentsElement = new Element("online-agents");
        for (final User onlineAgent : onlineAgents) {
            Set<HuntGroup> groupsByUser = userStatusKeeper.getGroupsByUser(onlineAgent);
            Element userElement = onlineAgent.toXml();
            userElement.setAttribute("state", userStateKeeper.getUserState(onlineAgent).getName().toLowerCase());
            userElement.setAttribute("state-time", formatTime(System.currentTimeMillis() - userStateKeeper.getStatusTime(onlineAgent)));
            for (final HuntGroup huntGroup : huntGroups) {
                Element huntGroupElement = huntGroup.toXml();
                if (groupsByUser.contains(huntGroup)) {
                    huntGroupElement.setAttribute("active", "true");
                } else {
                    huntGroupElement.setAttribute("active", "false");
                }
                userElement.addContent(huntGroupElement);
                List<Call> completedCallsByUser = callManager.getCompletedCallsByUser(onlineAgent);
                List<Call> filteredCompletedCalls = new ArrayList<>();
                Date from = new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1));
                for (final Call call : completedCallsByUser) {
                    if (call.getCreationTime().after(from)) {
                        filteredCompletedCalls.add(call);
                    }
                }
                userElement.setAttribute("completed-calls", filteredCompletedCalls.size() + "");
            }
            onlineAgentsElement.addContent(userElement);
        }
        response.addContent(onlineAgentsElement);
    }

    @Override
    public String getXslFilePath() {
        return "/xsl/agents_and_queues.xsl";
    }

    private Set<User> getOnlineUsersByRole(Set<User> onlineUsers, UserRole role) {
        Set<User> onlineSupervisors = new HashSet<>();
        for (final User activeUser : onlineUsers) {
            if (activeUser.getRole() == role) {
                onlineSupervisors.add(activeUser);
            }
        }
        return onlineSupervisors;
    }

    private void addHuntGroups(Element response, List<HuntGroup> groups) {

        Map<HuntGroup,Integer> agentsByGroup = userStatusKeeper.getAgentsByGroup();
        Map<HuntGroup, Integer> activeAgentsByGroup = userStatusKeeper.getActiveAgentsByGroup();
        Map<HuntGroup, Integer> holdingCalls = getHoldingCallsByHuntGroup();
        Map<HuntGroup, Integer> completedCalls = getCompletedCallsByGroup();
        Element groupsElement = new Element("hunt-groups");
        for (final HuntGroup huntGroup : groups) {

            Element groupElement = huntGroup.toXml();
            Integer agentsIn = agentsByGroup.get(huntGroup);
            if (agentsIn == null) {
                groupElement.setAttribute("agents-in", "0");
            } else {
                groupElement.setAttribute("agents-in", agentsIn.toString());
            }

            Integer availableAgents = activeAgentsByGroup.get(huntGroup);
            if (availableAgents == null) {
                groupElement.setAttribute("avl-agents", "0");
            } else {
                groupElement.setAttribute("avl-agents", availableAgents.toString());
            }

            Integer holdingCallsCount = holdingCalls.get(huntGroup);
            if (holdingCallsCount != null) {
                groupElement.setAttribute("calls-holding", holdingCallsCount.toString());
            }

            Call call = callManager.getLongestHoldingCallByHuntGroup(huntGroup);
            if (call != null) {
                long holdTime = new Date().getTime() - call.getCreationTime().getTime();
                groupElement.setAttribute("hold-time", formatTime(holdTime));
            }

            Integer completedCallsCount = completedCalls.get(huntGroup);
            if (completedCallsCount == null) {
                groupElement.setAttribute("completed-calls", "0");
            } else {
                groupElement.setAttribute("completed-calls", completedCallsCount.toString());
            }
            groupsElement.addContent(groupElement);
        }
        response.addContent(groupsElement);
    }

    private Map<HuntGroup, Integer> getHoldingCallsByHuntGroup() {
        List<Call> callsHolding = callManager.getCallsHolding();
        Map<HuntGroup, Integer> result = new HashMap<>();
        for (final Call call : callsHolding) {
            Integer value = result.get(call.getHuntGroup());
            if (value == null) {
                value = 1;
            } else {
                value++;
            }
            result.put(call.getHuntGroup(), value);
        }
        return result;
    }

    private Map<HuntGroup, Integer> getCompletedCallsByGroup() {
        List<Call> completedCalls = callManager.getCompletedCalls();
        Date from = new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1));
        Map<HuntGroup, Integer> result = new HashMap<>();
        for (final Call call : completedCalls) {
            if (!call.getCreationTime().after(from)) {
                continue;
            }
            Integer value = result.get(call.getHuntGroup());
            if (value == null) {
                value = 1;
            } else {
                value++;
            }
            result.put(call.getHuntGroup(), value);
        }
        return result;
    }

    private String formatTime(long millis) {
        return String.format("%02d:%02d:%02d",
                TimeUnit.MILLISECONDS.toHours(millis),
                TimeUnit.MILLISECONDS.toMinutes(millis) -
                        TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
                TimeUnit.MILLISECONDS.toSeconds(millis) -
                        TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
    }
}
